/*!
 * Lazy Load - JavaScript plugin for lazy loading images
 *
 * Copyright (c) 2007-2019 Mika Tuupola
 *
 * Licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Project home:
 *   https://appelsiini.net/projects/lazyload
 *
 * Version: 2.0.0-rc.2
 *
 */

const defaults = {
    src: 'data-src',
    srcset: 'data-srcset',
    selector: '.lazyload',
    root: null,
    rootMargin: '0px',
    threshold: 0
}

/**
* Merge two or more objects. Returns a new object.
* @private
* @param {Boolean}  deep     If true, do a deep (or recursive) merge [optional]
* @param {Object}   objects  The objects to merge together
* @returns {Object}          Merged values of defaults and options
*/
const extend = function () {
    let extended = {}
    let deep = false
    let i = 0
    let length = arguments.length

    /* Check if a deep merge */
    if (Object.prototype.toString.call(arguments[0]) === '[object Boolean]') {
        deep = arguments[0]
        i++
    }

    /* Merge the object into the extended object */
    let merge = function (obj) {
        for (let prop in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, prop)) {
                /* If deep merge and property is an object, merge properties */
                if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') {
                    extended[prop] = extend(true, extended[prop], obj[prop])
                } else {
                    extended[prop] = obj[prop]
                }
            }
        }
    }

    /* Loop through each object and conduct a merge */
    for (; i < length; i++) {
        let obj = arguments[i]
        merge(obj)
    }

    return extended
}

class LazyLoad {
    constructor(images, options) {
        this.settings = extend(defaults, options || {})
        this.images = images || document.querySelectorAll(this.settings.selector)
        this.observer = null
        this.init()
    }
    init() {
        /* Without observers load everything and bail out early. */
        if (typeof IntersectionObserver === 'undefined') {
            this.loadImages()
            return
        }

        let self = this
        let observerConfig = {
            root: this.settings.root,
            rootMargin: this.settings.rootMargin,
            threshold: [this.settings.threshold]
        }

        this.observer = new IntersectionObserver(function (entries) {
            Array.prototype.forEach.call(entries, function (entry) {
                if (entry.isIntersecting) {
                    self.observer.unobserve(entry.target)
                    let src = entry.target.getAttribute(self.settings.src)
                    let srcset = entry.target.getAttribute(self.settings.srcset)
                    if ('img' === entry.target.tagName.toLowerCase()) {
                        if (src) {
                            entry.target.src = src
                        }
                        if (srcset) {
                            entry.target.srcset = srcset
                        }
                    } else {
                        entry.target.style['background-image'] = `url('${src}')`
                    }
                }
            })
        }, observerConfig)

        Array.prototype.forEach.call(this.images, function (image) {
            self.observer.observe(image)
        })
    }
    loadAndDestroy() {
        if (!this.settings) { return }
        this.loadImages()
        this.destroy()
    }
    loadImages() {
        if (!this.settings) { return }

        let self = this
        Array.prototype.forEach.call(this.images, function (image) {
            let src = image.getAttribute(self.settings.src)
            let srcset = image.getAttribute(self.settings.srcset)
            if ('img' === image.tagName.toLowerCase()) {
                if (src) {
                    image.src = src
                }
                if (srcset) {
                    image.srcset = srcset
                }
            } else {
                image.style.backgroundImage = 'url(\'' + src + '\')'
            }
        })
    }
    destroy() {
        if (!this.settings) { return }
        this.observer.disconnect()
        this.settings = null
    }
}

const lazyload = function (images, options) {
    return new LazyLoad(images, options)
}

export default lazyload
